<?php
namespace Twoir\Service\Member;

use Exception;
use Twoir\Service\BaseMember;
use Twoir\System\Model\EnewsMember;
use Twoir\System\Model\EnewsMemberGroup;
use Twoir\System\Model\EnewsPublic;

/**
 * 会员注册
 *
 * time: 2024.05.01 cighsen02 3651872575
 * update: 2025.01.21
*/
class Register extends BaseMember
{
	// 允许进入字段
	const ALLOW = ['username','password','repassword','email','phno','groupid','key'];
	// 验证码key名称
	const REG_KEY_NAME = 'checkregkey';

	public function __construct()
	{
		global $public_r;
		// 关闭注册
		if(isset($public_r['register_ok']) && $public_r['register_ok']) throw new Exception('暂时关闭注册', 1200);
		// 验证时间段允许操作
		$this->checkTimeCloseDo('reg', $public_r['timeclosedo'], $public_r['timeclose']);
		// 验证IP
		$this->checkAccessDoIp('register');
		// 已经登陆不能注册
		if(getcvar('mluserid')) throw new Exception('已经登陆不能注册', 1200);
		// 验证来源
		$this->checkCanPostUrl($public_r['canposturl']);

        parent::__construct(false);
	}

	/**
	 * 邮箱注册
	 *
	 * @param array $add
	 * @return array|throw
	*/
	public function email($add)
	{
		global $public_r;
		// 参数过滤
		$add = $this->paramFilter($add);

		return [];
	}

	/**
	 * 用户名注册
	 *
	 * @param array $add
	 * @return array|throw
	*/
	public function userName(array $add): array
	{
		global $public_r, $level_r, $ecms_config;
		// 参数过滤
		$add 		= $this->paramFilter($add);

		$username	= isset($add['username']) && $add['username'] ? trim($add['username']) : null;
		$password	= isset($add['password']) && $add['password'] ? trim($add['password']) : null;
		$username	= RepPostVar($username);
		$password	= RepPostVar($password);
		$email		= isset($add['email']) && $add['email'] ? addslashes(RepPostStr($add['email'])) : null;
		$email		= RepPostVar($email);
        $phno       = isset($add['phno']) && $add['phno'] ? RepPostVar($add['phno']) : null;

		if(!$username) throw new Exception('用户名不能为空', 9200);

        if(!$password) throw new Exception('密码不能为空', 9200);

        if (3 != $public_r['regmustef']) {

            // 邮箱必填
            if(1 == $public_r['regmustef']){

                if(!$email) throw new Exception('邮箱不能为空', 9200);
            }
            // 手机必填
            elseif(2 == $public_r['regmustef']){

                if(!$phno) throw new Exception('手机不能为空', 9200);
            }
            // 邮箱和手机必填
            else {

                if(!$email || !$phno) throw new Exception('手机和邮箱不能为空', 9200);
		    }
        }

		if (!isset($add['repassword']) && empty($add['repassword'])) throw new Exception('重复密码不能为空', 9200);
		// 验证码
		if($public_r['regkey_ok']){

			if (!isset($add['key']) || empty($add['key'])) throw new Exception('验证码不能为空', 9200);

			$this->checkShowKey(static::REG_KEY_NAME, $add['key'], 1);
		}
		// 会员组
		$groupid = isset($add['groupid']) && $add['groupid'] ? (int)$add['groupid'] : $this->returnMemberDefGroupid();

		$this->checkMemberGroupCanReg($groupid);

        // 认证码
        if(isset($level_r[$groupid]['regps']) && $level_r[$groupid]['regps'])
        {
            if($level_r[$groupid]['regps'] !== $add['mg_regps'])
            {
                throw new Exception('认证码不对', 9200);
            }
        }

		// IP
		$regip		= egetip();
        $eipf       = egetipfrom();
		$regipport	= egetipport();

		extract(EnewsPublic::getOne(['id' => 1], ['min_userlen','max_userlen','min_passlen','max_passlen','regretime','regclosewords','regemailonly','regphonly']));

		$userlen = strlen($username);
		if($userlen < $min_userlen || $userlen > $max_userlen) throw new Exception('用户名长度过长或过短', 9200);

		//密码字数
		$passlen = strlen($password);
		if($passlen < $min_passlen || $passlen > $max_passlen) throw new Exception('密码长度过长或过短', 9200);

		if($add['repassword'] !== $password) throw new Exception('两次密码不一致', 9200);

		if(!chemail($email)) throw new Exception('邮箱格式错误', 9200);

        if(!echphno($phno)) throw new Exception('手机格式错误', 9200);

		if(str_contains($username, '|') || str_contains($username, '*')) throw new Exception('请勿使用“|”、“*”字符', 9200);

		// 同一IP注册
		$this->checkIpRegTime($regip, $regretime);
		// 保留用户
		$this->toCheckCloseWord($username, $regclosewords, 'RegHaveCloseword');
		$username = RepPostStr($username);
		// 重复用户
		if(EnewsMember::getCount(['username' => $username]) > 0) throw new Exception('此用户名已经存在，请重输', 9200);
		// 重复邮箱
		if($regemailonly && EnewsMember::getCount(['email' => $email]) > 0) throw new Exception('此邮箱已经被注册', 9200);
        // 重复手机
		if($regphonly && EnewsMember::getCount(['phno' => $phno]) > 0) throw new Exception('此邮箱已经被注册', 9200);
		// 注册时间
		$lasttime		= time();
		$registertime	= $this->returnAddMemberRegtime();
		$rnd			= make_password(EcmsRandInt(20,36));//产生随机密码
        $rndt           = make_password(EcmsRandInt(20,32));
		$userkey		= eReturnMemberUserKey();
		//密码
		$truepassword	= $password;
		$salt			= $this->returnMemberSalt();
		$password		= eDoMemberPw($password, $salt);
		//审核
		$checked		= $this->returnGroupChecked($groupid);
		if($checked && $public_r['regacttype']) $checked = 0;
		$checked		= (int)$checked;
		// 新增数据
		$insert 		= [
			'username'		=> $username,
			'password'		=> $password,
			'rnd'			=> $rnd,
			'email'			=> $email,
			'registertime'	=> $registertime,
			'groupid'		=> $groupid,
			'userfen'		=> $public_r['reggetfen'],
			'userdate'		=> 0,
			'money'			=> 0,
			'zgroupid'		=> 0,
			'havemsg'		=> 0,
			'checked'		=> $checked,
			'salt'			=> $salt,
			'userkey'		=> $userkey,
            'isot'          => 1,
            'phno'          => $phno,
            'mustdo'        => 0,
            'rndt'          => $rndt,

			'spacestyleid'	=> ReturnGroupSpaceStyleid($groupid),
			'regip'			=> $regip,
			'lasttime'		=> $lasttime,
			'lastip'		=> $regip,
			'loginnum'		=> 1,
			'regipport'		=> $regipport,
			'lastipport'	=> $regipport,
            'eipf'          => $eipf
		];

		if(0 < $userid = EnewsMember::insert($insert)) {

			//清空验证码
			$this->emptyShowKey(static::REG_KEY_NAME);

			$logincookie = $ecms_config['member']['regcookietime'] ? $lasttime + $ecms_config['member']['regcookietime'] : 0;

			esetcookie("mlusername", $username, $logincookie);
			esetcookie("mluserid", $userid, $logincookie);
			esetcookie("mlgroupid", $groupid, $logincookie);
			esetcookie("mlrnd", $rnd, $logincookie);
            esetcookie("mlrndt", $rndt, $logincookie);
			//验证符
			$this->qGetLoginAuthstr($userid, $username, $rnd, $groupid, $logincookie);
			esetcookie("returnurl", '');

			unset($insert['password'], $insert['userkey'], $insert['salt']);

			return [
				'userid' => $userid
			] + $insert;
		}

		return [];
	}

	/**
	 * 手机注册，在做之前请在主表增加phone字段
	 *
	 * @param array $add
	 * @return array
	*/
	public function phone($add)
	{
		global $public_r;

		// 参数过滤
		$add = $this->paramFilter($add);

		return [];
	}

	/**
	 * 返回是否审核
	 *
	 * @param int $groupid
	*/
	protected function returnGroupChecked(int $groupid): int
	{
		global $level_r;

		return isset($level_r[$groupid]['regchecked']) && 1 == $level_r[$groupid]['regchecked'] ? 0 : 1;
	}

	/**
	 * 验证包含字符
	 *
	 * @param string $word
	 * @param string $closestr
	 * @param string $mess
     *
	 * @throws Exception
	*/
	protected function toCheckCloseWord(string $word, string $closestr, string $mess)
	{
		if($closestr && $closestr !== '|'){

			$checkr	= explode('|', $closestr);
			$ckcount= count($checkr);

			for($i = 0; $i < $ckcount; $i++){

				if(isset($checkr[$i])){

					if(false !== stristr($checkr[$i],'##')){//多字

						$morer = explode('##', $checkr[$i]);
						if(false !== stristr($word, $morer[0]) && false !== stristr($word, $morer[1])){

							throw new Exception('用户名包含禁用字符', 9200);
						}
					}
					else{

						if(false !== stristr($word, $checkr[$i])) throw new Exception('用户名包含禁用字符', 9200);
					}
				}
			}
		}
	}

	/**
	 * 验证注册时间
	 *
	 * @param string $ip
	 * @param int $time
	 * @return string
     *
     * @throws Exception
	*/
	protected function checkIpRegTime(string $ip, int $time)
	{
		if(empty($time)) return '';

		if(empty($user = EnewsMember::getOne(['regip' => $ip], ['userid'], '', 'userid DESC')))	return '';

		if(empty($user = EnewsMember::getOne(['userid' => $user['userid']], ['userid', 'registertime']))) return '';

		if(time() - $this->returnMemberIntRegtime($user['registertime']) <= $time * 3600) throw new Exception('同一IP不能重复注册', 9200);

        return '';
	}

	/**
	 * 返回SALT
	 *
	 * @return string
	*/
	protected function returnMemberSalt(): string
	{
		global $ecms_config;

        if(isset($ecms_config['member']['saltnum']) && $ecms_config['member']['saltnum'] < 0)
        {
            $n = 0 - $ecms_config['member']['saltnum'];
            $n = EcmsRandInt($n, 12);

            return make_password($n);
        }

		return make_password($ecms_config['member']['saltnum']);
	}

	/**
	 * 返回当前注册时间
	 *
	 * @param int
	*/
	protected function returnAddMemberRegtime(): int
	{
		global $ecms_config;

		return empty($ecms_config['member']['regtimetype']) ? date('Y-m-d H:i:s') : time();
	}

	/**
	 * 返回注册时间(int)
	 *
	 * @param string
	 * @return int
	*/
	protected function returnMemberIntRegtime(string $regtime): int
	{
		global $ecms_config;

		return empty($ecms_config['member']['regtimetype']) ? to_time($regtime) : $regtime;
	}

	/**
	 * 检查用户组是否可注册
	 *
	 * @param int $groupid
     *
	 * @throws Exception
	*/
	protected function checkMemberGroupCanReg(int $groupid)
	{
		if($groupid && 0 === EnewsMemberGroup::getCount(['groupid' => $groupid, 'canreg' => 1])){

			throw new Exception('当前会员不允许注册', 9200);
		}
	}

	/**
	 * 验证来源
	 *
	 * @param string $canposturl
     *
	 * @throws Exception
	*/
	protected function checkCanPostUrl(string $canposturl)
	{
		if($canposturl){

			$r		= explode("\r\n", $canposturl);
			$count	= count($r);
			$b 		= false;
			for($i = 0; $i < $count; $i++){

				if(isset($_SERVER['HTTP_REFERER']) && false !== strstr($_SERVER['HTTP_REFERER'], $r[$i])){

					$b = true;
					break;
				}
			}

			if(!$b) throw new Exception('请从网站提交数据', 1200);
		}
	}

	/**
	 * 验证提交IP
	 *
	 * @param string $do
     *
	 * @throws Exception
	*/
	protected function checkAccessDoIp(string $do)
	{
		$pr = EnewsPublic::getOne(['id' => 1], ['opendoip', 'closedoip', 'doiptype']);

		if(isset($pr['doiptype']) && false !== strstr($pr['doiptype'], ',' . $do . ','))
		{
			$userip = egetip();

			// 允许IP
			if(isset($pr['opendoip']) && $pr['opendoip']){

				foreach(explode("\n", $pr['opendoip']) as $ctrlip){

					if(preg_match("/^(" . preg_quote(($ctrlip = trim($ctrlip)), '/') . ")/", $userip)){

						throw new Exception('当前ip已经禁止注册', 1200);
					}
				}
			}
			// 禁止IP
			if(isset($pr['closedoip']) && $pr['closedoip']){

				foreach(explode("\n", $pr['closedoip']) as $ctrlip){

					if(preg_match("/^(" . preg_quote(($ctrlip = trim($ctrlip)), '/') . ")/", $userip)){

						throw new Exception('当前ip已经禁止注册', 1200);
					}
				}
			}
		}
	}

	/**
	 * 验证操作时间
	 *
	 * @param string $do
	 * @param string $timeclosedo
     *
	 * @throws Exception
	*/
	protected function checkTimeCloseDo(string $do, string $timeclosedo, string $timeclose)
	{
		if(false !== stristr($timeclosedo, ',' . $do . ',')) throw new Exception('当前区间关闭注册', 1200);

		if(false !== strstr($timeclose, ',' . date('G') . ',')) throw new Exception('当前时间关闭注册', 1200);
	}
}
